<template>
  <div class="fast-transfer">
    <!-- 左侧 -->
    <div class="transfer-item">
      <div class="header">
        <span class="title">{{ titles[0] }}</span>
        <a-checkbox :indeterminate="indeterminate" v-model="checkAll" @change="onCheckAllChange">
          <span class="light"> {{ leftCheckedTotal }}</span
          >/{{ leftTotal }} 项
        </a-checkbox>
        <div class="extra">
          <slot name="left-extra"></slot>
        </div>
      </div>
      <div class="body">
        <div class="search-warp" v-if="showSearch[0]">
          <a-input-search
            class="custom-input-search"
            v-model="leftKeyword"
            placeholder="请输入搜索内容"
            @search="onLeftSearch"
            @keyup.enter="onLeftSearch(leftKeyword)"
          />
        </div>
        <div class="option-list">
          <div class="empty" v-if="list.length == 0">暂无数据</div>
          <a-checkbox-group v-model="leftValue" @change="onCheckOneChange">
            <div v-for="item in list" :key="item.value" class="checkbox-item">
              <a-checkbox :disabled="item.disabled" :value="item.value"> {{ item.label }} </a-checkbox>
            </div>
          </a-checkbox-group>
        </div>
      </div>
    </div>
    <!-- 右侧 -->
    <div class="transfer-item">
      <div class="header">
        <span
          ><span class="title">{{ titles[1] }}</span
          >{{ rightTotal }} 项</span
        >
        <a-button size="small" class="clear-btn" type="text" @click="onRightClear"> 全部清除 </a-button>
      </div>
      <div class="body">
        <div class="search-warp" v-if="showSearch[1]">
          <a-input-search
            class="custom-input-search"
            v-model="rightKeyword"
            placeholder="请输入搜索内容"
            @search="onRightSearch"
            @keyup.enter="onRightSearch(rightKeyword)"
          />
        </div>
        <div class="option-list">
          <div class="empty" v-if="tempRightList.length == 0">暂无数据</div>
          <div class="option-item" v-for="(item, index) in tempRightList" :key="item.value">
            <div class="label">{{ item.label }}</div>
            <div class="btn" @click="onRightDel(item, index)">
              <icon-close />
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'FastTransfer',
    props: {
      modelValue: Array,
      dataSource: [Array, Function],
      selectedList: [Array],
      titles: {
        type: Array,
        default: () => {
          return ['未选择', '已选择']
        },
      },
      showSearch: {
        type: Array,
        default: () => {
          return [true, true]
        },
      },
    },
    emits: ['update:modelValue', 'change'],
    data() {
      return {
        // 全选
        indeterminate: false,
        checkAll: false,
        // left
        leftKeyword: undefined,
        list: [],
        leftValue: [],
        lastLeftValue: [],
        // right
        rightKeyword: undefined,
        rightList: [],
        tempRightList: [], // 搜索时保留原始的list
      }
    },
    computed: {
      leftCheckedTotal() {
        return this.leftValue.length
      },
      leftTotal() {
        return this.list.length
      },
      rightTotal() {
        return this.rightList.length
      },
      // 是否远程
      remoteFlag() {
        return typeof this.dataSource == 'function'
      },
    },
    watch: {
      dataSource: {
        handler() {
          this.asyncGetList()
        },
        immediate: true,
      },
      // 默认选中，初始化右侧列表(保证list、value加载完成)
      selectedList: {
        handler(val) {
          this.$nextTick(async () => {
            const modelValue = this.modelValue
            if (val && modelValue) {
              if (val.length != modelValue.length) {
                if (this.remoteFlag) {
                  const list = await this.dataSource()
                  val = list.filter((item) => modelValue.includes(item.value))
                } else {
                  val = this.dataSource.filter((item) => modelValue.includes(item.value))
                }
              }
              this.onCheckOneChange([...modelValue], false)
              this.rightList = [...val]
              this.tempRightList = this.rightList
            }
          })
        },
        immediate: true,
      },
    },
    methods: {
      // 同步获取list
      async asyncGetList() {
        if (this.remoteFlag) {
          this.list = await this.dataSource()
          return
        }
        this.list = this.dataSource
      },
      // 左侧搜索，调api
      async onLeftSearch(val) {
        if (this.remoteFlag == false) {
          if (!val) {
            this.list = this.dataSource
          } else {
            this.list = this.dataSource.filter((item) => {
              return item.label.toLowerCase().indexOf(val.toLowerCase()) >= 0
            })
          }
        } else {
          this.list = await this.dataSource(val)
        }
        const rightValue = this.rightList.map((item) => item.value)
        const leftValue = rightValue.filter((item) => {
          return this.list.findIndex((litem) => litem.value == item) != -1
        })
        this.onCheckOneChange(leftValue, false)
      },
      // 左侧自定义搜索事件
      onLeftSearchByRemote(data) {
        this.list = data
        const rightValue = this.rightList.map((item) => item.value)
        const leftValue = rightValue.filter((item) => {
          return this.list.findIndex((litem) => litem.value == item) != -1
        })
        this.onCheckOneChange(leftValue, false)
      },
      // 左侧全选
      onCheckAllChange() {
        if (this.leftCheckedTotal == this.leftTotal) {
          this.onCheckOneChange([])
          return
        }
        if (this.indeterminate) {
          this.checkAll = true
        }
        const allValue = this.list.map((item) => item.value)
        this.onCheckOneChange(allValue)
      },
      // 左侧选择
      onCheckOneChange(val = [], setRight = true) {
        this.leftValue = val
        const addList = [],
          reduceList = []
        val.forEach((item) => {
          if (this.lastLeftValue.includes(item) == false) {
            addList.push(item)
          }
        })
        this.lastLeftValue.forEach((item) => {
          if (val.includes(item) == false) {
            reduceList.push(item)
          }
        })
        this.lastLeftValue = val
        // 顶部全选状态
        if (val.length) {
          this.checkAll = false
          this.indeterminate = true
          if (val.length >= this.list.length) {
            this.checkAll = true
            this.indeterminate = false
          }
        } else {
          this.indeterminate = false
          this.checkAll = false
        }
        // 修改右侧选中列表
        if (setRight) {
          this.setSelectedList(addList, reduceList)
        }
      },
      // 右侧搜索（前端过滤）
      onRightSearch(val) {
        // 没有值使用原list
        if (!val) {
          this.tempRightList = this.rightList
          return
        }
        this.tempRightList = this.tempRightList.filter((item) => {
          return item.label.toLowerCase().indexOf(val.toLowerCase()) >= 0
        })
      },
      // 右侧清空
      onRightClear() {
        // 回调空数组
        this.rightList = []
        this.onCheckOneChange([])
      },
      // 右侧删除
      onRightDel(item, index) {
        this.tempRightList.splice(index, 1)
        // 删除原始数据
        if (this.tempRightList != this.rightList) {
          const rightIndex = this.rightList.findIndex((ritem) => ritem.value == item.value)
          this.rightList.splice(rightIndex, 1)
        }
        // 移除左侧选中
        const leftIndex = this.leftValue.findIndex((litem) => litem == item.value)
        if (leftIndex != -1) {
          this.leftValue.splice(leftIndex, 1)
        }
        // 改变左侧视图，但关闭设置右侧数据
        this.onCheckOneChange(this.leftValue, false)
        this.emitEvent()
      },
      // 设置右侧值，添加左侧已选中的，移除左侧未选中的；左侧不存在的保留
      setSelectedList(addList, reduceList) {
        // 增加
        addList.forEach((item) => {
          const addOne = this.list.find((litem) => litem.value == item)
          this.rightList.push(addOne)
        })
        // 删除
        reduceList.forEach((item) => {
          const delIndex = this.rightList.findIndex((ritem) => ritem.value == item)
          this.rightList.splice(delIndex, 1)
        })

        this.rightKeyword = undefined
        this.tempRightList = this.rightList
        this.emitEvent()
      },
      emitEvent() {
        const rightList = this.rightList
        const rightValue = rightList.map((item) => item.value)
        this.$emit('update:modelValue', rightValue)
        this.$emit('change', rightValue, rightList)
      },
    },
  }
</script>

<style lang="less" scoped>
  :deep(.arco-input-error) {
    background-color: var(--color-fill-2) !important;
    border-color: transparent !important;
  }
  :deep(.arco-checkbox-label) {
    word-break: break-all;
  }
  .fast-transfer {
    position: relative;
    display: flex;
    padding-bottom: 4px;

    .transfer-item {
      flex: 1;
      border-radius: 4px;
      border: 1px solid #ebebeb;
      .header {
        display: flex;
        align-items: center;
        padding: 0 16px;
        border-bottom: 1px solid #f2f3f5;
        background: #f2f3f5;
        font-size: 14px;
        font-weight: 400;
        color: #1f2334;
        line-height: 20px;
        height: 40px;
        .title {
          font-size: 14px;
          font-weight: 600;
          color: #1f2334;
          line-height: 20px;
          margin-right: 16px;
        }
        .light {
          color: #1890ff;
        }
        .extra {
          float: right;
        }
      }
      .body {
        padding-bottom: 1px;
        .checkbox-item {
          margin: 12px 0;
        }
        .search-warp {
          padding: 12px 12px 0;
          .custom-input-search {
            background-color: #fff;
            height: 32px;
            border-radius: 2px 2px 2px 2px;
            border: 1px solid #f2f3f5;
            :deep(svg) {
              color: #bfbfbf;
              font-size: 16px;
            }
          }
        }
        .custom-search {
          padding: 8px 16px 0;
        }
        .option-list {
          padding: 0px 12px;
          height: 320px;
          overflow-y: auto;
          font-size: 14px;
          font-weight: 400;
          color: #1f2334;
          line-height: 20px;

          .empty {
            margin-top: 100px;
            text-align: center;
            color: rgba(0, 0, 0, 0.25);
          }

          .option-item {
            display: flex;
            align-items: flex-start;
            margin: 12px 0;

            .label {
              flex: 1;
              margin-right: 10px;
              font-size: 14px;
              font-weight: 400;
              color: rgba(0, 0, 0, 0.65);
              line-height: 20px;
              word-break: break-all;
            }
            .btn {
              width: 20px;
              height: 20px;
              line-height: 20px;
              text-align: center;
              background: #f2f2f2;
              border-radius: 2px;
              color: #171616;
              cursor: pointer;
              &:hover {
                color: #1890ff;
              }
            }
          }
        }
      }

      &:last-child {
        margin-left: 20px;
        .header {
          display: flex;
          justify-content: space-between;
        }
      }
      .clear-btn:hover {
        background-color: var(--color-fill-3) !important;
      }
    }
  }
</style>
